#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
    #include <Windows.h>
#endif
#include "xusb_common.h"
#include "protocol.h"
#include "scsi.h"
#include "pd_linux.h"
#ifndef TIME_OUT
    #define TIME_OUT 2*1000
#endif

#ifndef _WIN32
    #ifndef INVALID_HANDLE_VALUE
        #define INVALID_HANDLE_VALUE (void*)-1
    #endif
#endif


unsigned short calc_checksum(unsigned char *p, int len)
{
    int i;
    unsigned short checksum = 0;

    for(i = 0; i < len; i++)
    {
        checksum += p[i];
    }

    return checksum;
}

T_U32 CheckSum(unsigned char *param, T_U32 count, T_U32 bit)
{
    T_U32 i, mask, sum;

    mask = ~(0xFFFFFFFFUL << bit);

    for(i = 0, sum = 1; i < count; i++)
    {
        if(bit == 8)
        {
            sum += *((T_U8 *)param + i);
        }
        else if(bit == 16)
        {
            sum += *((T_U16 *)param + i);
        }
        else if(bit == 32)
        {
            sum += *((T_U32 *)param + i);
        }
        else
        {
            break;
        }
    }

    if(bit == 8)
    {
        sum &= 0xFF;
    }
    else if(bit == 16)
    {
        sum &= 0xFFFF;
    }
    else if(bit == 32)
    {
        sum &= 0xFFFFFFFF;
    }
    else
    {
        sum &= 0xFF;
    }

    //sum &= mask;

#if 0
    if(bit == 8)
    {
        if(sum == *((T_U8 *)param + i))

        {
            TRACE("CheckSum ret = %d, [%d, %d]!\n", ret, sum, *((T_U8 *)param + i));
        }
    }
    else if(bit == 16)
    {
        if(sum == *((T_U16 *)param + i))

        {
            TRACE("CheckSum ret = %d, [%d, %d]!\n", ret, sum, *((T_U16 *)param + i));
        }
    }
    else if(bit == 32)
    {
        if(sum == *((T_U32 *)param + i))

        {
            TRACE("CheckSum ret = %d, [%d, %d]!\n", ret, sum, *((T_U32 *)param + i));
        }
    }
    else
    {
        TRACE("Invalid check parameter!\n");
    }
#endif

    return sum;
}

int build_command_packet(unsigned char *buffer, int buflen, unsigned char cmd, int param)
{
    PCommandProto proto = NULL;

    if(buffer == NULL)
    {
        return PARAM_INVALID;
    }

    if(buflen < sizeof(TCommandProto))
    {
        return PARAM_ERROR;
    }

    memset(buffer, 0, buflen);
    proto = (PCommandProto) buffer;
    proto->CH1 = ACK_CH1;
    proto->CH2 = ACK_CH2;
    proto->DevID = DEV_ID;
    proto->InParam = param;
    proto->Cmd = cmd;
    proto->CheckSum = calc_checksum(buffer, 10);

    return 1;
}

int send_data(void *handle, unsigned char *data, int data_size)
{
    int sendsize = 0;
    int position = 0;
    unsigned char sendBuf[SEND_SIZE];

    do
    {
        ///最大一次性只发送64KB数据.6个字节为协议保留.
        if(data_size > 65530)
        {
            sendsize = 65530;
        }
        else
        {
            sendsize = data_size;
        }

        ///生成指定数据格式
        build_data_packet(sendBuf, sendsize + 6, data + position);

        int ret = Scsi_Write(handle, sendBuf, sendsize + 6, CMD_TIME_OUT);
        if(1 != ret)
        {
            return ret;
        }

        position += sendsize;
        data_size -= sendsize;
    } while(data_size > 0);

    return 1;
}


int build_data_packet(unsigned char *buffer, int size, unsigned char *image)
{
    unsigned char *p = NULL;
    unsigned short checksum = 0;
    int imagesize = 0;

    //flag
    buffer[0] = DATA_DH1;
    buffer[1] = DATA_DH2;
    //device id
    buffer[2] = 0x01;
    buffer[3] = 0x00;

    imagesize = size - 6;
    p = buffer + 4;
    memcpy(p, image, imagesize);
    checksum = calc_checksum(buffer, imagesize + 4);

    p = buffer + 4 + imagesize;
    *((unsigned short *)p) = checksum;

    //printf("before checksum:0x%02x, checksum: %d(%02x %02x)\n", *(p-1), checksum, *p, *(p+1));
    return 1;
}


void print_param_value(int param)
{
#if 0
    unsigned char *p = (unsigned char *)&param;

    printf("outparam:%d(0x%02x 0x%02x 0x%02x 0x%02x)\n",
           param, *(p + 0), *(p + 1), *(p + 2), *(p + 3));
#endif
}

int check_ack_packet(unsigned char *buffer, int buflen)
{
    PACKProto ack = NULL;
    int sum = 0;

    if(buffer == NULL)
    {
        //printf("[%s %d] Invalid null pointer.\n", __FILE__,
        //      __LINE__);
        return PARAM_INVALID;
    }

    if(buflen < sizeof(TACKProto))
    {
        ///printf("[%s %d] Invalid buffer length:%d.\n", __FILE__,
        //      __LINE__, buflen);
        return PARAM_ERROR;
    }

#ifdef _DEBUG
    printf("[%s %d] buffer len:%d\n", __FILE__,
           __LINE__, buflen);
    for(sum = 0; sum < sizeof(TACKProto); sum++)
    {
        printf("0x%02x ", buffer[sum]);
    }
    printf("\n");
#endif
    ack = (PACKProto) buffer;

    if(ack->RH1 != ACK_CH1 || ack->RH2 != ACK_CH2)
    {
        //printf("[%s %d] Invalid protocol head:%d,%d\n", __FILE__,
        //      __LINE__, ack->RH1, ack->RH2);
        return ACK_HEADER_ERROR;
    }

    if(ack->DevID != DEV_ID)
    {
        //printf("[%s %d] Invalid device id:%d\n", __FILE__,
        //      __LINE__, ack->DevID);
        return DEVID_ERROR;
    }

    sum = calc_checksum(buffer, 10);
    if(ack->CheckSum != sum)
    {
        //printf("[%s %s %d] checksum error.from:%d,local:%d\n", __FILE__, __LINE__, ack->CheckSum, sum);
        return CHECK_SUM_ERROR;
    }

    if(ack->Ack != ACK_OK)
    {
        //printf("[%s %d] Response error:0x%04x\n", __FILE__,
        //  __LINE__, ack->Ack);
        //print_param_value(ack->OutParam);
        return ACK_ERROR;
    }

    return 1;
}

int check_ack_datapacket(unsigned char *buffer, int buflen)
{
    PDataHeader ack = NULL;
    int checksum1 = 0;
    int checksum2 = 0;

    if(buffer == NULL)
    {
        //printf("[%s %d] Invalid null pointer.\n", __FILE__,
        //      __LINE__);
        return PARAM_INVALID;
    }

    if(buflen < sizeof(TDataHeader))
    {
        //printf("[%s %d] Invalid buffer length:%d.\n", __FILE__,
        //      __LINE__, buflen);
        return PARAM_ERROR;
    }

    ack = (PDataHeader) buffer;
    if(ack->dh1 != DATA_DH1 || ack->dh2 != DATA_DH2)
    {
        //printf("[%s %d] Invalid protocol head:%d,%d\n", __FILE__,
        //      __LINE__, ack->dh1, ack->dh2);
        return ACK_HEADER_ERROR;
    }

    if(ack->DevID != DEV_ID)
    {
        //printf("[%s %d] Invalid device id:%d\n", __FILE__,
        //      __LINE__, ack->DevID);
        return DEVID_ERROR;
    }

    checksum1 = calc_checksum(buffer, buflen - 2);
    checksum2 = *((unsigned short *)(buffer + buflen - 2));
    if(checksum1 != checksum2)
    {
        //printf("[%s %d] checksum error.from:%d,local:%d\n", __FILE__, __LINE__, checksum2, checksum1);
        return CHECK_SUM_ERROR;
    }

    return 1;
}

int send_command(void *handle, unsigned char cmd, int InParam, int *OutParam)
{
    TCommandProto proto;
    TACKProto ack;
    memset(&ack, 0, sizeof(TACKProto));
    int ret = 0;

#ifdef _WIN32
    if(handle == INVALID_HANDLE_VALUE)
    {
        return PARAM_INVALID;
    }
#else
    if(handle == NULL)
    {
        return PARAM_INVALID;
    }
#endif

    ret = build_command_packet((unsigned char *)&proto, sizeof(TCommandProto), cmd, InParam);

    if(1 == ret)
    {
        ret = Scsi_Write(handle, (unsigned char *)&proto, sizeof(TCommandProto), CMD_TIME_OUT);

        if(ret)
        {
            ret = Scsi_Read(handle, (unsigned char *)&ack, sizeof(TACKProto), CMD_TIME_OUT);
            if(ret)
            {
                ret = check_ack_packet((unsigned char *)&ack, sizeof(TACKProto));
                if(1 == ret)
                {
                    if(NULL != OutParam)
                    {
                        *OutParam = ack.OutParam;
                    }
                }
                else if(ACK_ERROR == ret)
                {
                    *OutParam = ack.OutParam;
                }
            }
            else
            {
#ifndef _WIN32
                ret = -3;
#else
                ret = GetLastError();
                ret = ret > 0 ? (-99) : ret;
#endif
            }
        }
        else
        {
#ifndef _WIN32
            ret = -3;
#else
            ret = GetLastError();
            ret = ret > 0 ? (-99) : ret;
#endif
        }
    }

#ifndef _WIN32
    if(ret < 0)
    {
        return ret;
    }
#endif
    return 1 == ret ? 1 : ack.OutParam;
}

int send_data(void *handle, unsigned char *data, int size, int *OutParam, int timeOut)
{
    TACKProto ack;
    int ret = 0;
    unsigned short encryptFlag;
    unsigned char *buffer = NULL;
    unsigned char *dataBuffer = NULL;


    if(handle == INVALID_HANDLE_VALUE)
    {
        return PARAM_INVALID;
    }

    buffer = (unsigned char *)malloc(4096 + 256);
    if(NULL == buffer)
    {
        return -1;
    }

    dataBuffer = (unsigned char *)malloc(4096 + 256);
    if(NULL == dataBuffer)
    {
        free(buffer);
        return -1;
    }


    memset(dataBuffer, 0x00, 4096 + 256);

    encryptFlag = 0x80; //增加加密标识
    memcpy(dataBuffer, &encryptFlag, sizeof(encryptFlag));
    memcpy(dataBuffer + sizeof(encryptFlag), data, size);
    size += sizeof(encryptFlag);

    ret = build_data_packet((unsigned char *)buffer, size + 6, dataBuffer);
    if(1 == ret)
    {
        ret = Scsi_Write(handle, buffer, size + 6, TIME_OUT);

        if(ret)
        {
            Sleep(1);   //延时
            ret = Scsi_Read(handle, (unsigned char *)&ack, sizeof(TACKProto), TIME_OUT);
            if(ret)
            {
                ret = check_ack_packet((unsigned char *)&ack, sizeof(TACKProto));

                if(1 == ret)
                {
                    if(NULL != OutParam)
                    {
                        *OutParam = ack.OutParam;
                    }
                }
            }
            else
            {
#ifndef _WIN32
                ret = -3;
#else
                ret = GetLastError();
                ret = ret > 0 ? (-99) : ret;
#endif
            }
        }
        else
        {
#ifndef _WIN32
            ret = -3;
#else
            ret = GetLastError();
            ret = ret > 0 ? (-99) : ret;
#endif
        }
    }

    free(buffer);
    free(dataBuffer);

    return ret;
}



int get_bulk_data(void *handle, unsigned char cmd, int InParam, unsigned char *buffer, int len)
{
    int ret = 0, index = 0;
    int InParamEx = 0;
    int OutParam = 0;
    int readSize = 0;
    int bufferSize = len;
    TCommandProto proto;
    unsigned char *buf = NULL;
    int position = 0;

    buf = (unsigned char *)malloc(64 * 1024);

    //只发一次，让其进入取像阶段
    InParamEx = InParam + ((index++) << 24);
    build_command_packet((unsigned char *)&proto, sizeof(TCommandProto), cmd, InParamEx);
    ret = Scsi_Write(handle, (unsigned char *)&proto, sizeof(TCommandProto), CMD_TIME_OUT);

    do
    {
        ///最大一次性只发送64KB数据.6个字节为协议保留.
        if(bufferSize > 65530)
        {
            readSize = 65530;
        }
        else
        {
            readSize = bufferSize;
        }

        //InParamEx = InParam + ((index++) << 24);

        //ret = build_command_packet((unsigned char *)&proto, sizeof(TCommandProto), cmd, InParamEx);

        if(1 == ret)
        {
            //ret = Scsi_Write(handle, &proto, sizeof(TCommandProto), TIME_OUT);

            //if(ret)
            //{
            ret = Scsi_Read(handle, buf, readSize + 6, CMD_TIME_OUT);
            if(ret)
            {
                //不做校验处理,usb发送时自己会做CRC校检，因此使用usb的校验，加快速度
                //ret = check_ack_datapacket(buf, readSize+4);
                //if(1 == ret)
                //{
                //  memcpy(buffer+position, buf, readSize);
                //}

                memcpy(buffer + position, buf + 4, readSize);
            }
            else
            {
#ifndef _WIN32
                ret = -3;
#else
                ret = GetLastError();
                ret = ret > 0 ? (-99) : ret;
#endif
            }
            //}
            //else
            //{
            //  ret = GetLastError();
            //}
        }

        if(1 == ret)
        {
            position += readSize;
            bufferSize -= readSize;
        }
        else
        {
            bufferSize = -1;
        }
    } while(bufferSize > 0);

    free(buf);

    return ret;
}


int read_data(void *handle, unsigned char *data, int size, int *OutParam)
{
    int ret = 0;
    unsigned char *buffer = NULL;


    if(handle == INVALID_HANDLE_VALUE)
    {
        return PARAM_INVALID;
    }

    buffer = (unsigned char *)malloc(4096 + 256);
    if(NULL == buffer)
    {
        return -1;
    }

    ret = Scsi_Read(handle, buffer, size + 6, TIME_OUT);
    if(ret)
    {
        ret = check_ack_datapacket(buffer, size + 6);
    }
    else
    {
#ifndef _WIN32
        ret = -3;
#else
        ret = GetLastError();
        ret = ret > 0 ? (-99) : ret;
#endif
    }

    memcpy(data, buffer + sizeof(TDataHeader), size);
    free(buffer);

    return ret;
}

